package com.jbp.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jbp.dao.WalletPayDao;
import com.jbp.exception.RRException;
import com.jbp.model.*;
import com.jbp.service.*;
import com.jbp.utils.HttpClient;
import com.jbp.utils.UUIDUtil;
import com.jbp.utils.payUtils.AlipayUtils;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jbp.utils.PageUtils;
import com.jbp.utils.Query;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 充值(WalletPay)表服务实现类
 * @author cc
 * @since 2020-07-02 11:45:11
 */
@Service
public class WalletPayServiceImpl extends ServiceImpl<WalletPayDao, WalletPay> implements WalletPayService {
    @Autowired
    private WalletRecordService walletRecordService;

    @Autowired
    private WalletService walletService;

    @Autowired
    private PropertyService propertyService;

    @Autowired
    private AlipayConfigService alipayConfigService;

    @Autowired
    private WechatConfigService wechatConfigService;
    @Autowired
    private ApplePayGoodsService applePayGoodsService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WalletPay> queryWrapper = new QueryWrapper<>();
        if (params.get("userId") != null){
            queryWrapper.eq("user_id",params.get("userId"));
        }
        queryWrapper.orderByDesc("create_time");
        IPage<WalletPay> page = this.page(
                new Query<WalletPay>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    //创建订单
    @Override
    public Map<String, Object> createOrder(WalletPay walletPay) {
        //生成唯一订单号
        String orderNo = createOrderNo(walletPay.getPayType());
        while (getByOrderId(orderNo)!=null) {
            orderNo = createOrderNo(walletPay.getPayType());
        }

        walletPay.setOrderNo(orderNo);
        walletPay.setStatus("create");
        String goodName = propertyService.getV("pay_good_name");
        if ("alipay".equals(walletPay.getPayType())) {
            AlipayConfig alipayConfig = alipayConfigService.getAlipayAccount(walletPay.getMoney().intValue());
            Map<String, Object> result = new HashMap<>();
            result.put("alipayInfo", AlipayUtils.getAliPayInfo(goodName, walletPay.getOrderNo(), walletPay.getMoney().doubleValue() + "",
                    alipayConfig, propertyService.getV("alipay_notify_url")));

            walletPay.setAccountId(alipayConfig.getId());
            walletPay.setPayType("alipay");
            save(walletPay);
            return result;
        } else if ("wechat".equals(walletPay.getPayType())) {
            WechatConfig wechatConfig = wechatConfigService.getWechatAccount(walletPay.getMoney().intValue());
            String notifyUrl = propertyService.getV("wechat_notify_url") +"/"+wechatConfig.getId();
            Map<String, Object> result = WechatPayUtil.getPayMap(goodName, walletPay.getOrderNo(), walletPay.getMoney().intValue() * 100, notifyUrl, wechatConfig);
            walletPay.setAccountId(wechatConfig.getId());
            walletPay.setPayType("wechat");
            save(walletPay);
            return result;
        }
        throw new RRException("充值类型错误");
    }

    ///通知回调
    @Override
    @Transactional
    public synchronized void aliNotify(Map<String, Object> params) {
        //订单号
        String orderNo = (String) params.get("out_trade_no");
        WalletPay walletPay = getByOrderId(orderNo);
        if (walletPay != null) {
            AlipayConfig alipayConfig = alipayConfigService.getById(walletPay.getAccountId());
            if (alipayConfig == null) {
                throw new RRException("回调异常");
            }
            String APPID = alipayConfig.getAppid();
            String appId = (String) params.get("app_id");
            if (!APPID.equals(appId)) {
                throw new RRException("回调异常");
            }
        }

        //交易状态 TRADE_SUCCESS 成功
        String trade_status	 = (String) params.get("trade_status");
        if (!"TRADE_SUCCESS".equals(trade_status)) {
            throw new RRException("订单状态错误");
        }
        //订单金额
        BigDecimal totalAmount = BigDecimal.valueOf(Double.valueOf((String) params.get("total_amount")));

        if (walletPay==null) {
            throw new RRException("订单不存在");
        }
        if (!"create".equals(walletPay.getStatus())) {
            throw new RRException("订单状态不对");
        }

        //检查订单金额
        if (totalAmount.compareTo(walletPay.getMoney())!=0) {
            throw new RRException("订单金额异常");
        }

        payResult(walletPay);

    }

    private void payResult(WalletPay walletPay) {
        //完成订单
        walletPay.setStatus("success");
        updateById(walletPay);

        Wallet wallet = walletService.getByUserId(walletPay.getUserId());
        BigDecimal after = wallet.getXinzuan().add(walletPay.getXinzuan());

        //添加余额变动记录
        WalletRecord walletRecord = new WalletRecord();
        walletRecord.setUserId(walletPay.getUserId());
        walletRecord.setAddType("add");
        walletRecord.setOperationType("pay");
        walletRecord.setCoinType("xinzuan");
        walletRecord.setBeforeBalance(wallet.getXinzuan());
        walletRecord.setChangeBalance(walletPay.getXinzuan());
        walletRecord.setAfterBalance(after);
        walletRecordService.save(walletRecord);

        //增加钱包余额
        wallet.setXinzuan(after);
        walletService.updateWithLock(wallet);
    }

    @Override
    public BigDecimal getPayToday(Integer userId) {
        return getBaseMapper().getPayToday(userId);
    }

    @Override
    @Transactional
    public void wechatNotify(Map<String, Object> heads, String body, Integer configId) {
        //验签
        String timeStamp = (String) heads.get("Wechatpay-Timestamp");
        String nonce = (String) heads.get("Wechatpay-Nonce");
        String signature = (String) heads.get("Wechatpay-Signature");
//        System.out.println("timeStamp:"+timeStamp);
//        System.out.println("nonce:"+nonce);
//        System.out.println("signature:"+signature);
//        System.out.println("body:"+body);

        String str = timeStamp+"\n"+nonce+"\n"+body+"\n";
        WechatConfig wechatConfig = wechatConfigService.getById(configId);
        if (wechatConfig==null) {
            throw new RRException("微信配置不存在");
        }
        try {
            PrivateKey merchantPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(wechatConfig.getPrivateKey())));
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(wechatConfig.getMchid(), new PrivateKeySigner(wechatConfig.getMchSerialNo(), merchantPrivateKey)), wechatConfig.getApiV3Key().getBytes("utf-8"));
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(verifier.getValidCertificate());
            sign.update(str.getBytes());
            boolean flag = sign.verify(java.util.Base64.getDecoder().decode(signature));
            if (!flag) {
                throw new RRException("验签失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RRException("获取证书失败");
        }

        //解密
        JSONObject jsonObject = JSON.parseObject(body);
        if (!"TRANSACTION.SUCCESS".equals(jsonObject.getString("event_type"))) {
            throw new RRException("支付失败");
        }
        JSONObject resource = jsonObject.getJSONObject("resource");
        String nonce2 = resource.getString("nonce");
        String associated_data = resource.getString("associated_data");
        String ciphertext = resource.getString("ciphertext");

        try {
            String result = WechatPayUtil.decryptToString(associated_data.getBytes(), nonce2.getBytes(), ciphertext, wechatConfig.getApiV3Key().getBytes());
            JSONObject req = JSON.parseObject(result);
            if (wechatConfig.getAppid().equals(req.getString("appid"))
                    && wechatConfig.getMchid().equals(req.getString("mchid"))
                    && "SUCCESS".equals(req.getString("trade_state")) ) {
                String out_trade_no = req.getString("out_trade_no");
                WalletPay walletPay = getByOrderId(out_trade_no);
                if (walletPay == null) {
                    System.out.println("订单不存在");
                    throw new RRException("订单不存在");
                }
                if (!"create".equals(walletPay.getStatus())) {
                    System.out.println("订单状态不对");
                    throw new RRException("订单状态不对");
                }
                int money = req.getJSONObject("amount").getInteger("total");
                if (walletPay.getMoney().compareTo(BigDecimal.valueOf(money).divide(BigDecimal.valueOf(100)))!=0) {
                    System.out.println("支付金额错误");
                    throw new RRException("支付金额错误");
                }

                payResult(walletPay);
            } else {
                System.out.println("解密失败");
                throw new RRException("解密失败");
            }
        } catch (Exception e) {
            System.out.println("解密失败");
            throw new RRException("解密失败");
        }
    }

    @Override
    @Transactional
    public boolean applePayResult(String body, Integer userId) {
        try {
            String url = "https://buy.itunes.apple.com/verifyReceipt";
            String betaUrl = "https://sandbox.itunes.apple.com/verifyReceipt";
            String result = HttpClient.doPostJson(body, null, url);
            JSONObject object = JSON.parseObject(result);
            if (21007==object.getInteger("status")) {
                result = HttpClient.doPostJson(body, null, betaUrl);
                object = JSON.parseObject(result);
            }
            if (0==object.getInteger("status")) {
                String productId = object.getJSONObject("receipt").getJSONArray("in_app").getJSONObject(0).getString("product_id");
                ApplePayGoods goods = applePayGoodsService.getByProductId(productId);

                if(goods != null) {
                    String orderNo = object.getJSONObject("receipt").getJSONArray("in_app").getJSONObject(0).getString("transaction_id");
                    WalletPay walletPay = getByOrderId(orderNo);
                    if (walletPay == null) {
                        walletPay = new WalletPay();
                        walletPay.setUserId(userId);
                        walletPay.setPayType("apple");
                        walletPay.setOrderNo(orderNo);
                        walletPay.setStatus("create");
                        walletPay.setMoney(BigDecimal.valueOf(goods.getMoney()));
                        walletPay.setXinzuan(BigDecimal.valueOf(goods.getXinzuan()));
                        save(walletPay);

                        payResult(walletPay);
                    }
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**根据订单号取账单*/
    private WalletPay getByOrderId(String orderNo) {
        QueryWrapper<WalletPay> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no", orderNo);
        return  getOne(wrapper);
    }

    /**生成订单号*/
    private String createOrderNo(String type) {
        String orderPre = propertyService.getV("pay_order_pre")+type;
        String time = new SimpleDateFormat("MMddHHmmss").format(new Date());
        String orderNo = orderPre+time+UUIDUtil.getNumber(4);
        return orderNo;
    }


}